#include "ResManager.h"
#include "Material.h"
#include "Texture.h"
#include "..\renderer\VertexDeclaration.h"
#include "..\utils\Log.h"
#include "Sound.h"
#include "..\utils\FileUtils.h"
#include <cctype>
#include "..\lib\container\String.h"
#include "BitmapFont.h"

namespace ds {

const ResourceHandle UNKNOWN_HANDLE = ResourceHandle(0,RS_INVALID);
// ---------------------------------------------------
// ResManager
// ---------------------------------------------------
ResManager::ResManager() {
	addName("NO_VALID_RESOURCE",UNKNOWN_HANDLE);
}


ResManager::~ResManager() {
	LOGC(logINFO,"ResManager") << "deleting all resources";		
	m_Textures.deleteContents();
	m_Materials.deleteContents();
	m_Sounds.deleteContents();
	m_Shaders.deleteContents();
	for ( uint32 i = 0; i < m_Fonts.num(); ++i ) {
		SAFE_RELEASE(m_Fonts[i]);
	}
}

ResourceHandle ResManager::createMaterial() {
	Material* m = new Material("NAME");
	uint32 index = m_Materials.append(m);	
	return ResourceHandle(index,RS_MATERIAL);
}

ResourceHandle ResManager::createMaterial(const char* name) {
	LOGC(logINFO,"ResManager") << "creating empty material " << name;
	Material* m = new Material(name);
	uint32 index = m_Materials.append(m);	
	ResourceHandle rh(index,RS_MATERIAL);
	addName(name,rh);
	return rh;
}

ResourceHandle ResManager::createMaterial(const char* name,const char* textureName,bool useAlpha) {
	ResourceHandle rh = findByName(textureName,RS_TEXTURE);
	assert(rh.m_Type != RS_INVALID);
	return createMaterial(name,rh,useAlpha);
}

ResourceHandle ResManager::createMaterial(const char* name,ResourceHandle textureHandle,bool useAlpha) {
	LOGC(logINFO,"ResManager") << "create material - name: " << name << " using textureHandle " << textureHandle.m_Index;
	Material* m = new Material(name,textureHandle);
	m->setAlpha(useAlpha);
	uint32 index = m_Materials.append(m);	
	ResourceHandle rh(index,RS_MATERIAL);
	addName(name,rh);
	return rh;
}

Material* ResManager::getMaterial(ResourceHandle handle) {
	if ( handle.m_Type == RS_MATERIAL ) {
		return m_Materials[handle.m_Index];
	}	
	return 0;
}

Material* ResManager::getMaterialByName(const char* name) {
	ResourceHandle rh = findByName(name,RS_MATERIAL);
	if ( rh.m_Type != RS_INVALID ) {
		return getMaterial(rh);
	}	
	return 0;
}

Texture* ResManager::getTexture(ResourceHandle handle) {
	if ( handle.m_Type == RS_TEXTURE) {
		return m_Textures[handle.m_Index];
	}
	return 0;
}

Texture* ResManager::getTextureByName(const char* name) {
	ResourceHandle rh = findByName(name,RS_TEXTURE);
	if ( rh.m_Type != RS_INVALID ) {
		return getTexture(rh);
	}	
	return 0;
}

Sound* ResManager::getSound(ResourceHandle handle) {
	if ( handle.m_Type == RS_SOUND) {
		return m_Sounds[handle.m_Index];
	}
	return 0;
}

Sound* ResManager::getSoundByName(const char* name) {
	ResourceHandle rh = findByName(name,RS_SOUND);
	if ( rh.m_Type != RS_INVALID ) {
		return getSound(rh);
	}
	return 0;
}

ResourceHandle ResManager::loadFont(const char* name,const char* fontName,int size,bool bold) {
	LOGC(logINFO,"ResManager") << "Loading font " << fontName << " size " << size;
	ID3DXFont *font;
	UINT type = FW_NORMAL;
	if ( bold ) {
		type = FW_BOLD;
	}
	HDC hDC = GetDC(gEngine->getHWND());
	int nHeight = -MulDiv(size, GetDeviceCaps(hDC, LOGPIXELSY), 72);
	HR(D3DXCreateFontA(gEngine->getDevice(),nHeight,0,type,0,false,DEFAULT_CHARSET,
		DEFAULT_QUALITY,ANTIALIASED_QUALITY,
		DEFAULT_PITCH|FF_DONTCARE,
		fontName,&font));
	uint32 index = m_Fonts.append(font);			
	ResourceHandle rh = ResourceHandle(index,RS_FONT);
	addName(name,rh);
	return rh;
}

ResourceHandle ResManager::loadFont(const char* name,const char* fileName,const char* fontName,int size,bool bold) {
	LOGC(logINFO,"ResManager") << "Loading font " << fontName << " from file " << fileName << " size " << size;
	ID3DXFont *font;
	UINT type = FW_NORMAL;
	if ( bold ) {
		type = FW_BOLD;
	}
	HDC hDC = GetDC(gEngine->getHWND());
	int nHeight = -MulDiv(size, GetDeviceCaps(hDC, LOGPIXELSY), 72);
	LOGC(logINFO,"ResManager") << "Calculated height: " << nHeight;
	int count = AddFontResourceEx(fileName,FR_PRIVATE,0);
	LOGC(logINFO,"ResManager") << "fonts loaded " << count;
	HR(D3DXCreateFontA(gEngine->getDevice(),nHeight,0,type,0,false,DEFAULT_CHARSET,
		DEFAULT_QUALITY,ANTIALIASED_QUALITY,
		DEFAULT_PITCH|FF_DONTCARE,
		fontName,&font));
	uint32 index = m_Fonts.append(font);		
	ResourceHandle rh = ResourceHandle(index,RS_FONT);
	addName(name,rh);
	return rh;
}

ID3DXFont* ResManager::getFont(ResourceHandle handle) {
	if ( handle.m_Type == RS_FONT) {
		return m_Fonts[handle.m_Index];
	}
	return 0;
}

ID3DXFont* ResManager::getFont(const char* name) {
	ResourceHandle rh = findByName(name,RS_FONT);
	if ( rh.m_Type != RS_INVALID ) {
		return getFont(rh);
	}
	return 0;
}


// FIXME: remove this
ResourceHandle ResManager::createTexture(const char* name,const char* fileName,int width,int height) {
	return createTexture(name,fileName);
}

ResourceHandle ResManager::createTexture(const char* name,const char* fileName) {
	if ( !contains(name,RS_TEXTURE) ) {
		Texture* tr = new Texture(fileName);
		if ( tr->loadTexture(fileName) ) {
			LOGC(logINFO,"ResManager") << "Creating texture " << name;			
			uint32 index = m_Textures.append(tr);			
			ResourceHandle rh = ResourceHandle(index,RS_TEXTURE);
			addName(name,rh);
			return rh;
		}
		else {
			LOGC(logERROR,"ResManager") << "Cannot load texture";
		}
	}
	else {
		LOGC(logINFO,"ResManager") << "Texture " << name << " is already inside ResManager";
	}
	return ResourceHandle();
}

ResourceHandle ResManager::createTexture(const char* name,Texture* texture) {
	if ( !contains(name,RS_TEXTURE) ) {
		LOGC(logINFO,"ResManager") << "Creating texture " << name;
		uint32 index = m_Textures.append(texture);		
		ResourceHandle rh = ResourceHandle(index,RS_TEXTURE);
		addName(name,rh);
		return rh;
	}
	else {
		LOGC(logINFO,"ResManager") << "Texture " << name << " is already inside ResManager";
	}
	return ResourceHandle();
}

ResourceHandle ResManager::loadSound(const char* name,const char* fileName) {
	Sound *s = new Sound(name);
	LOGC(logINFO,"ResManager") << "loading sound " << fileName;
	int ret = s->loadWavFile(fileName);
	if ( ret > 0 ) {		
		uint32 index = m_Sounds.append(s);
		ResourceHandle rh(index,RS_SOUND);
		addName(name,rh);
		gEngine->getAudioManager().createAudioBuffer(name);		
		return rh;
	}
	else {
		LOGC(logERROR,"ResManager") << "Error while loading Sound";
		return UNKNOWN_HANDLE;
	}
}

// --------------------------------------------------------------
//  VertexDeclaration handling
// --------------------------------------------------------------
ResourceHandle ResManager::createVertexDeclaration(const char* name) {
	VertexDeclaration* vd = new VertexDeclaration(name);
	uint32 idx = m_VertexDeclarations.append(vd);
	ResourceHandle rh(idx,RS_VERTEX_DECLARATION);	
	addName(name,rh);
	return rh;
}

VertexDeclaration* ResManager::getVertexDeclaration(ResourceHandle handle) {
	if ( handle.m_Type == RS_VERTEX_DECLARATION) {
		return m_VertexDeclarations[handle.m_Index];
	}
	return 0;
}

VertexDeclaration* ResManager::getVertexDeclaration(const char* name) {	
	ResourceHandle handle = findByName(name,RS_VERTEX_DECLARATION);
	if ( handle.m_Type != RS_INVALID ) {
		return getVertexDeclaration(handle);
	}
	return 0;
}

ResourceHandle ResManager::createRenderTarget(int index,const char* materialName) {	
	LOGC(logINFO,"ResManager") << "creating render target - index " << index << " material " << materialName;
	RenderTarget* rt = new RenderTarget();
	ResourceHandle textureHandle = rt->create(gEngine->getRenderer()->getGraphicsDevice(),index);	
	if ( !contains(materialName,RS_MATERIAL)) {
		createMaterial(materialName,textureHandle,true);
	}
	else {
		// update material
		LOGC(logINFO,"ResManager") << "found existing material - replacing texture";
		Material* m = getMaterialByName(materialName);
		m->replaceTexture(0,textureHandle);
	}
	String name = "RT";
	name.append(index);
	uint32 idx = m_RenderTargets.append(rt);	
	ResourceHandle nrh(idx,RS_RENDERTARGET);
	addName(name.c_str(),nrh);	
	return nrh;
}

RenderTarget* ResManager::getRenderTarget(ResourceHandle handle) {
	if ( handle.m_Type == RS_RENDERTARGET) {
		return m_RenderTargets[handle.m_Index];
	}
	return 0;
}
// --------------------------------------------------------------
//
// --------------------------------------------------------------
ResourceHandle ResManager::loadShader(const char* name,const char* fileName,const char* techName) {
	Shader* shader = new Shader(name,fileName,techName);
	if ( shader != 0 ) {
		shader->init();
		uint32 index = m_Shaders.append(shader);
		ResourceHandle rh(index,RS_SHADER);
		addName(name,rh);
		return rh;
	}
	return UNKNOWN_HANDLE;
}

ResourceHandle ResManager::addShader(const char* name,Shader* shader) {
	if ( shader != 0 ) {
		LOGC(logINFO,"ResManager") << "adding created shader " << name;
		shader->init();
		uint32 index = m_Shaders.append(shader);
		ResourceHandle rh(index,RS_SHADER);
		addName(name,rh);
		return rh;
	}
	return ResourceHandle();
}

// --------------------------------------------------------------
//
// --------------------------------------------------------------
void ResManager::assignShader(const char* shaderName,const char* materialName) {
	Material* m = getMaterialByName(materialName);
	assert(m!=0);
	ResourceHandle shaderHandle = findByName(shaderName,RS_SHADER);
	m->addShader(shaderHandle);
}

// --------------------------------------------------------------
//
// --------------------------------------------------------------
void ResManager::assignShader(ResourceHandle shaderHandle,ResourceHandle materialHandle) {
	Material* m = getMaterial(materialHandle);
	assert(m != 0);
	m->addShader(shaderHandle);	
}

// --------------------------------------------------------------
//
// --------------------------------------------------------------
Shader& ResManager::getShaderByName(const char* name) {
	ResourceHandle rh = findByName(name,RS_SHADER);
	return *m_Shaders[rh.m_Index];
}

// --------------------------------------------------------------
//
// --------------------------------------------------------------
Shader& ResManager::getShader(ResourceHandle shaderHandle) {
	return *m_Shaders[shaderHandle.m_Index];
}

// --------------------------------------------------------------
// Contains
// --------------------------------------------------------------
bool ResManager::contains(const char* name) {
	IdString hash = string::murmur_hash(name);
	for ( uint32 i = 0; i < m_NameList.num(); ++i ) {
		if ( m_NameList[i].hash == hash ) {
			return true;
		}
	}
	return false;
}

bool ResManager::contains(const char* name,int type) {
	IdString hash = string::murmur_hash(name);
	for ( uint32 i = 0; i < m_NameList.num(); ++i ) {
		if ( m_NameList[i].hash == hash && m_NameList[i].handle.m_Type == type ) {
			return true;
		}
	}
	return false;
}

// --------------------------------------------------------------
// Name mapping
// --------------------------------------------------------------
void ResManager::addName(const char* name,ResourceHandle handle) {
	LOGC(logINFO,"ResManager") << "adding name mapping " << name << " handle " << handle.m_Index << " type " << handle.m_Type;
	assert(!contains(name,handle.m_Type));
	NameMapping nm;
	nm.hash = string::murmur_hash(name,strlen(name),0);
	nm.handle = handle;
	strncpy(nm.name,name,128);
	m_NameList.append(nm);
}

ResourceHandle& ResManager::findByName(const char* name,int type) {
	IdString hash = string::murmur_hash(name,strlen(name),0);
	for ( uint32 i = 0; i < m_NameList.num(); ++i ) {
		if ( m_NameList[i].hash == hash && m_NameList[i].handle.m_Type == type ) {
			return m_NameList[i].handle;
		}
	}
	LOGC(logINFO,"ResManager") << "no matching resource found for " << name << " " << type;
	return m_NameList[0].handle;
}

ResourceHandle ResManager::createBitmapFont(const char* name,uint32 startChar,uint32 charHeight,uint32 startX,uint32 startY,uint32 textureSize) {
	if ( !contains(name,RS_BITMAP_FONT) ) {
		BitmapFont* bf = new BitmapFont(name,startChar,charHeight,startX,startY,textureSize);
		LOGC(logINFO,"ResManager") << "Creating bitmap font " << name;			
		uint32 index = m_BitmapFonts.append(bf);			
		ResourceHandle rh = ResourceHandle(index,RS_BITMAP_FONT);
		addName(name,rh);
		return rh;		
	}
	else {
		LOGC(logINFO,"ResManager") << "Bitmap font " << name << " is already inside ResManager";
	}
	return ResourceHandle();
}

// --------------------------------------------------------------
//
// --------------------------------------------------------------
BitmapFont* ResManager::getBitmapFontByName(const char* name) {
	ResourceHandle rh = findByName(name,RS_BITMAP_FONT);
	return m_BitmapFonts[rh.m_Index];
}

// --------------------------------------------------------------
//
// --------------------------------------------------------------
BitmapFont* ResManager::getBitmapFont(ResourceHandle handle) {
	return m_BitmapFonts[handle.m_Index];
}
// --------------------------------------------------------------
// Debugging
// --------------------------------------------------------------
void ResManager::debug() {
	LOGC(logINFO,"ResManager") << "Materials          : " << m_Materials.num();
	LOGC(logINFO,"ResManager") << "Textures           : " << m_Textures.num();
	LOGC(logINFO,"ResManager") << "Sounds             : " << m_Sounds.num();
	LOGC(logINFO,"ResManager") << "Shader             : " << m_Shaders.num();
	LOGC(logINFO,"ResManager") << "Fonts              : " << m_Fonts.num();
	LOGC(logINFO,"ResManager") << "VertexDeclarations : " << m_VertexDeclarations.num();
	LOGC(logINFO,"ResManager") << "BitmapFonts        : " << m_BitmapFonts.num();
	LOGC(logINFO,"ResManager") << "RenderTargets      : " << m_RenderTargets.num();
	for ( uint32 i = 0; i < m_NameList.num();++i ) {
		NameMapping* nm = &m_NameList[i];
		LOGC(logINFO,"ResManager") << nm->name << " : " << translateType(nm->handle);
	}
}

std::string ResManager::translateType(ResourceHandle handle) {
	std::string ret;
	switch ( handle.m_Type ) {
		case RS_NONE : ret.append("RS_NONE");break;
		case RS_MATERIAL : ret.append("RS_MATERIAL");break;
		case RS_TEXTURE : ret.append("RS_TEXTURE");break;
		case RS_SHADER : ret.append("RS_SHADER");break;
		case RS_SOUND : ret.append("RS_SOUND");break;
		case RS_MESH : ret.append("RS_MESH");break;
		case RS_RENDERTARGET : ret.append("RS_RENDERTARGET");break;
		case RS_FONT : ret.append("RS_FONT");break;
		case RS_VERTEX_DECLARATION : ret.append("RS_VERTEX_DECLARATION");break;
		case RS_BITMAP_FONT : ret.append("RS_BITMAP_FONT");break;
		default : ret.append("UNKNOWN");break;
	}
	return ret;
}

}
